Skip to main content

Error Handling

In Go, error handling is explicit — meaning you have to manually check and handle errors instead of relying on exceptions (like in Java, Python, or C#). Errors in Go are values, and they follow the "error is just another return value" philosophy.

Go uses the built-in error interface for errors.

type error interface {
Error() string
}

If a function can fail, it usually returns two values:

  1. The result (if successful, otherwise zero value)
  2. An error (if something went wrong, otherwise nil)

Creating and Returning Errors

We can create an error using the errors.New() function from the errors package or fmt.Errorf().

import (
"errors"
"fmt"
)

func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("division by zero is not allowed")
}
return a / b, nil
}

func main() {
result, err := divide(10, 0)

if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println("Result:", result)
}
}

Using fmt.Errorf for Formatted Errors

fmt.Errorf() is like errors.New() but allows formatting:

err := fmt.Errorf("failed to open file %s: %v", filename, originalErr)

Custom Error Types

You can create your own error type by implementing the Error() method.

type MyError struct {
Code int
Message string
}

func (e *MyError) Error() string {
return fmt.Sprintf("Error %d: %s", e.Code, e.Message)
}

func doSomething() error {
return &MyError{Code: 404, Message: "Not Found"}
}

func main() {
err := doSomething()
if err != nil {
fmt.Println(err)
}
}
  • Allows attaching extra information (like codes, timestamps, etc.)
  • Makes error checking more specific.

Error Wrapping (%w)

Go 1.13+ supports error wrapping, letting you chain error contexts.

import (
"errors"
"fmt"
)

func readConfig() error {
return errors.New("file missing")
}

func loadApp() error {
return fmt.Errorf("loadApp failed: %w", readConfig())
}

func main() {
err := loadApp()
fmt.Println(err) // Prints: loadApp failed: file missing
}

Checking Specific Errors (errors.Is and errors.As)

Sometimes you want to check what kind of error happened.

import (
"errors"
"fmt"
)

var ErrNotFound = errors.New("not found")

func search() error {
return ErrNotFound
}

func main() {
err := search()

if errors.Is(err, ErrNotFound) {
fmt.Println("Item not found.")
}
}

Panic vs Error

  • Error → Recoverable problems; expected to happen sometimes.
  • Panic → Unrecoverable problems; program should stop immediately.
  • Use panic() rarely (e.g., initialization failures, corrupted state).
  • You can recover from panics using recover(), but that’s mainly for critical failure safety nets.